//
//  Client.m
//  Sample_AVAPIs
//
//  Created by tutk on 3/6/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "Client.h"
#import "IOTCAPIs.h"
#import "AVAPIs.h"
#import "AVIOCTRLDEFs.h"
#import "AVFRAMEINFO.h"
#import <sys/time.h>
#import <pthread.h>
#import "ATOpenAL.h"
#import "AudioPlayer.h"
#define MAX_PCM (32635)
#define AUDIO_BUF_SIZE	1024
#define VIDEO_BUF_SIZE	2000000
const uint8_t KStartCode[4] = {0, 0, 0, 1};
@interface Client ()
{
    
}
@property (nonatomic, strong) VTDecode         *vtDecoder;         //硬解码器
@property(nonatomic, strong) ATOpenAL *openAL;//播放器
@property(nonatomic, strong) AudioPlayer *audioPlayer;//
@end

@implementation Client

static id object;
 int _avMissPackage;

unsigned int _getTickCount() {
    
	struct timeval tv;
    
	if (gettimeofday(&tv, NULL) != 0)
        return 0;
    
	return (tv.tv_sec * 1000 + tv.tv_usec / 1000);
}

void *thread_ReceiveAudio(void *arg)
{
    NSLog(@"[thread_ReceiveAudio] Starting...");
    
    int avIndex = *(int *)arg;
    char buf[AUDIO_BUF_SIZE];
    unsigned int frmNo;
    int ret;
    FRAMEINFO_t frameInfo;
    
    while (1)
    {
        ret = avCheckAudioBuf(avIndex);
        if (ret < 0) break;
        if (ret < 3) // determined by audio frame rate
        {
            usleep(120000);
            continue;
        }
            
        ret = avRecvAudioData(avIndex, buf, AUDIO_BUF_SIZE, (char *)&frameInfo, sizeof(FRAMEINFO_t), &frmNo);
        
        if(ret == AV_ER_SESSION_CLOSE_BY_REMOTE)
        {
            NSLog(@"[thread_ReceiveAudio] AV_ER_SESSION_CLOSE_BY_REMOTE");
            break;
        }
        else if(ret == AV_ER_REMOTE_TIMEOUT_DISCONNECT)
        {
            NSLog(@"[thread_ReceiveAudio] AV_ER_REMOTE_TIMEOUT_DISCONNECT");
            break;
        }
        else if(ret == IOTC_ER_INVALID_SID)
        {
            NSLog(@"[thread_ReceiveAudio] Session cant be used anymore");
            break;    
        }
        else if (ret == AV_ER_LOSED_THIS_FRAME)
        {
//            NSLog(@"[thread_ReceiveAudio] AV_ER_LOSED_THIS_FRAME");
            continue;
        }
        //音频帧
        // Now the data is ready in audioBuffer[0 ... ret - 1]
        // Do something here
//        NSLog(@"Now the data is ready in audioBuffer[0 ... ret - 1]");
        if (ret > 0) {
            [object clientDidGetAudioBytes:buf size:ret timeStamp:frameInfo.timestamp];
        }

    }

    NSLog(@"[thread_ReceiveAudio] thread exit");
    return 0;
}

void *thread_ReceiveVideo(void *arg)
{
    NSLog(@"[thread_ReceiveVideo] Starting...");

    int avIndex = *(int *)arg;
    char *buf = malloc(VIDEO_BUF_SIZE);
    unsigned int frmNo;
	int actualFrameSize=0, expectedFrameSize=0;
	int actualInfoSize=0;
    int ret;
    FRAMEINFO_t frameInfo;
    
    while (1) 
    {
        ret = avRecvFrameData2(avIndex, buf, VIDEO_BUF_SIZE, &actualFrameSize, &expectedFrameSize, (char *)&frameInfo, sizeof(FRAMEINFO_t), &actualInfoSize, &frmNo);
        
        if(ret == AV_ER_DATA_NOREADY)
		{
			usleep(30000);
			continue;
		}
		else if(ret == AV_ER_LOSED_THIS_FRAME)
		{
            NSLog(@"Lost video frame NO[%d], [%@]", frmNo, frameInfo.flags?@"I":@"P");
//            _avMissPackage ++;
//            lostFrame = frameInfo.flags == 1 ? 1 : 2;
			continue;
		}
		else if(ret == AV_ER_INCOMPLETE_FRAME)
		{
			NSLog(@"Incomplete video frame NO[%d]", frmNo);
			continue;
		}
		else if(ret == AV_ER_SESSION_CLOSE_BY_REMOTE)
		{
			NSLog(@"[thread_ReceiveVideo] AV_ER_SESSION_CLOSE_BY_REMOTE");
			break;
		}
		else if(ret == AV_ER_REMOTE_TIMEOUT_DISCONNECT)
		{
			NSLog(@"[thread_ReceiveVideo] AV_ER_REMOTE_TIMEOUT_DISCONNECT");
			break;
		}
		else if(ret == IOTC_ER_INVALID_SID)
		{
			NSLog(@"[thread_ReceiveVideo] Session cant be used anymore");
			break;
		}
        //视频帧
//        if(frameInfo.flags == IPC_FRAME_FLAG_IFRAME)
//        {
//            // got an IFrame, draw it.
//        }
//
//        if(frameInfo.flags == IPC_FRAME_FLAG_PBFRAME) {
//            // got an P/B Frame, draw it.
//        }
        [object clientDidGetVideoBytes:buf size:actualFrameSize timeStamp:frameInfo.timestamp];
       
    }
    free(buf);
    NSLog(@"[thread_ReceiveVideo] thread exit");    
    return 0;
}

- (void)clientDidGetVideoBytes:(char *)frameBytes size:(UInt32)frameSize timeStamp:(UInt32)timeStamp
{
    //NSData *data = [NSData dataWithBytes:frameBytes length:frameSize];
    //NSLog(@"got an Frame, %@",data);
    uint8_t *bytes = (uint8_t *)frameBytes;
    int begin = 0;
    int len = 64;
    if (frameSize < len) {
        len = frameSize;
    }
    for (int i = 0; i < len - 4; i++) {
        if (bytes[i]==0x00 && bytes[i+1]==0x00 && bytes[i+2]==0x00 && bytes[i+3]==0x01) {
            if (i - 1 > begin) {
                [self.vtDecoder decodeWithBytes:(char *)(&frameBytes[begin]) length:(i - begin) timeStamp:timeStamp];
                begin = i;
            }
        } else if (i >= len - 5) {
            [self.vtDecoder decodeWithBytes:(char *)(&frameBytes[begin]) length:(frameSize - begin) timeStamp:timeStamp];
        }
    }
}


//NSData *data = [NSData dataWithBytes:frameBytes length:frameSize];
//NSLog(@"got an Frame, %@",data);
- (void)clientDidGetAudioBytes:(char *)frameBytes size:(UInt32)frameSize timeStamp:(UInt32)timeStamp
{
    char *pcmDataBuffer;//pcm的读文件数据区
    pcmDataBuffer = malloc(frameSize *2);
    int len = Client_G711_Decode(pcmDataBuffer, (unsigned char*)frameBytes, frameSize);
//    [self.openAL openAudio:pcmDataBuffer length:len];
    [self.audioPlayer addPCM:(void *)pcmDataBuffer length:len timeStamp:timeStamp];
    

    free(pcmDataBuffer);
}


int start_ipcam_stream (int avIndex) {
    
    int ret;
    unsigned short val = 0;
    
    if ((ret = avSendIOCtrl(avIndex, IOTYPE_INNER_SND_DATA_DELAY, (char *)&val, sizeof(unsigned short)) < 0))
    {
        NSLog(@"start_ipcam_stream_failed[%d]", ret);
        return 0;
    }
    
    SMsgAVIoctrlAVStream ioMsg;
    memset(&ioMsg, 0, sizeof(SMsgAVIoctrlAVStream));
    if ((ret = avSendIOCtrl(avIndex, IOTYPE_USER_IPCAM_START, (char *)&ioMsg, sizeof(SMsgAVIoctrlAVStream)) < 0))
    {
        NSLog(@"start_ipcam_stream_failed[%d]", ret);
        return 0;        
    }
    
    if ((ret = avSendIOCtrl(avIndex, IOTYPE_USER_IPCAM_AUDIOSTART, (char *)&ioMsg, sizeof(SMsgAVIoctrlAVStream)) < 0))
    {
        NSLog(@"start_ipcam_stream_failed[%d]", ret);
        return 0;        
    }
    
    return 1;
}

void *start_main (NSString *UID) {
    int ret, SID;
    
    NSLog(@"AVStream Client Start");
    
    ret = IOTC_Initialize2(0);
    NSLog(@"IOTC_Initialize() ret = %d", ret);
    
    if (ret != IOTC_ER_NoERROR) {
        NSLog(@"IOTCAPIs exit...");
        return NULL;
    }
    
    // alloc 4 sessions for video and two-way audio
    avInitialize(4);
    
    SID = IOTC_Get_SessionID();
    if (SID < 0)
    {
        NSLog(@"IOTC_Get_SessionID error [%d]", SID);
        return NULL;
    }
    ret = IOTC_Connect_ByUID_Parallel((char *)[UID UTF8String], SID);
    if (ret < 0)
    {
        NSLog(@"IOTC_Connect_ByUID_Parallel error [%d]", ret);
        return NULL;
    }
    
    printf("Step 2: call IOTC_Connect_ByUID_Parallel(%s) ret(%d).......\n", [UID UTF8String], ret);
    struct st_SInfo Sinfo;
    ret = IOTC_Session_Check(SID, &Sinfo);
    
    if (ret >= 0)
    {
        if(Sinfo.Mode == 0)
            printf("Device is from %s:%d[%s] Mode=P2P\n",Sinfo.RemoteIP, Sinfo.RemotePort, Sinfo.UID);
        else if (Sinfo.Mode == 1)
            printf("Device is from %s:%d[%s] Mode=RLY\n",Sinfo.RemoteIP, Sinfo.RemotePort, Sinfo.UID);
        else if (Sinfo.Mode == 2)
            printf("Device is from %s:%d[%s] Mode=LAN\n",Sinfo.RemoteIP, Sinfo.RemotePort, Sinfo.UID);
    }
    
    unsigned long srvType;
//    int avIndex = avClientStart(SID, "admin", "888888", 20000, &srvType, 0);
    int avIndex = avClientStart(SID, "admin", "admin123", 20000, &srvType, 0);
    printf("Step 3: call avClientStart(%d).......\n", avIndex);
    
    if(avIndex < 0)
    {
        printf("avClientStart failed[%d]\n", avIndex);
        return NULL;
    }
    
    if (start_ipcam_stream(avIndex)>0)
    {
        pthread_t ThreadVideo_ID, ThreadAudio_ID;
        pthread_create(&ThreadVideo_ID, NULL, &thread_ReceiveVideo, (void *)&avIndex);
        pthread_create(&ThreadAudio_ID, NULL, &thread_ReceiveAudio, (void *)&avIndex);
        pthread_join(ThreadVideo_ID, NULL);
        pthread_join(ThreadAudio_ID, NULL);
    }
    
    avClientStop(avIndex);
    NSLog(@"avClientStop OK");
    IOTC_Session_Close(SID);
    NSLog(@"IOTC_Session_Close OK");
    avDeInitialize();
    IOTC_DeInitialize();
    
    NSLog(@"StreamClient exit...");
    
    return NULL;

}

- (void)start:(NSString *)UID andDelegate:(id<VideoDecoderDelegate>)delegate{
    object = self;
    self.vtDecoder = [[VTDecode alloc] init];
    self.vtDecoder.delegate = (id<VideoDecoderDelegate>)delegate;
    
    
//    self.openAL = [[ATOpenAL alloc] init] ;
//    [self.openAL initOpenAl];
//
    self.audioPlayer = [[AudioPlayer alloc] init];
    self.audioPlayer.delegate = self;
    [self.audioPlayer setSampleRate:8000 channels:1 sampleBits:16];
    
    pthread_t main_thread;
    pthread_create(&main_thread, NULL, &start_main, (__bridge void *)UID);
    pthread_detach(main_thread);
}


int Client_G711_Decode(char* pRawData, unsigned char* pBuffer, int nBufferSize)
{
    int i=0;
    short *out_data = (short*)pRawData;
    for(i=0; i<nBufferSize; i++)
    {
        out_data[i] = Client_def_decode(pBuffer[i]);
    }
    return nBufferSize*2;
}

extern short Client_def_decode(unsigned char alaw)
{
    alaw ^= 0xD5;
    int sign = alaw & 0x80;
    int exponent = (alaw & 0x70) >> 4;
    int data = alaw & 0x0f;
    data <<= 4;
    data += 8;
    if (exponent != 0)
        data += 0x100;
    if (exponent > 1)
        data <<= (exponent - 1);

    return (short)(sign == 0 ? data : -data);
}

int G711_EnCode(unsigned char* pCodecBits, char* pBuffer, int nBufferSize)
{
    int i=0;
    short* buffer = (short*)pBuffer;
    for(i=0; i<nBufferSize/2; i++)
    {
        pCodecBits[i] = def_encode(buffer[i]);
    }
    return nBufferSize/2;
}

extern unsigned char def_encode(short pcm)
{
    int sign = (pcm & 0x8000) >> 8;
    if (sign != 0)
        pcm = -pcm;
    if (pcm > MAX_PCM) pcm = MAX_PCM;
    int exponent = 7;
    int expMask;
    for (expMask = 0x4000; (pcm & expMask) == 0
         && exponent>0; exponent--, expMask >>= 1) { }
    int mantissa = (pcm >> ((exponent == 0) ? 4 : (exponent + 3))) & 0x0f;
    unsigned char alaw = (unsigned char)(sign | exponent << 4 | mantissa);
    return (unsigned char)(alaw^0xD5);
}


@end
